package com.smartian.scan.filesearch.dispatchers;

import android.content.Context;
import android.util.Log;

import com.smartian.scan.filesearch.DispatcherManager;
import com.smartian.scan.filesearch.SearchTaskManager;
import com.smartian.scan.filesearch.bean.PathFile;
import com.smartian.scan.filesearch.bean.SearchResult;
import com.smartian.scan.filesearch.filters.RepeatFileFilter;
import com.smartian.scan.filesearch.impl.engine.FileSearchTaskEngine;
import com.smartian.scan.filesearch.impl.engine.MediaStoreQueryTaskEngine;
import com.smartian.scan.filesearch.interfaces.IDispatcher;
import com.smartian.scan.filesearch.interfaces.IFilter;
import com.smartian.scan.filesearch.interfaces.ISearchCallback;
import com.smartian.scan.filesearch.interfaces.ISearchTaskEngine;
import com.smartian.scan.filesearch.interfaces.ITaskCallback;
import com.smartian.scan.filesearch.task.exectors.TaskExecutors;
import com.smartian.scan.filesearch.task.runnable.TaskRunnable;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;

public class SearchTaskDispatcher implements IDispatcher, Runnable, ITaskCallback {

    private static final String TAG = "SEARCH_TASK";
    private List<String> beginPaths = null;
    private Queue<String> searchQueue = null;
    private long timeout = 0l;
    private List<IFilter> filters;
    private ISearchCallback callback;
    private volatile boolean isTerminal = false;
    private long startTime = 0l;

    private AtomicLong mTaskCount;
    private AtomicLong mFinishTaskCount;
    private RepeatFileFilter repeatFileFilter;

    public SearchTaskDispatcher() {
        this.searchQueue = new LinkedBlockingQueue<>();
        this.mTaskCount = new AtomicLong(0);
        this.mFinishTaskCount = new AtomicLong(0);
        this.repeatFileFilter = new RepeatFileFilter();
    }

    @Override
    public void setBeginPaths(List<String> dirs) {
        this.beginPaths = dirs;
    }

    @Override
    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }

    @Override
    public void setFilters(List<IFilter> filters) {
        this.filters = filters;
    }

    @Override
    public void setCallback(ISearchCallback callback) {
        this.callback = callback;
    }



    @Override
    public void start() {
        startTime = System.currentTimeMillis();
        TaskExecutors.THREAD_POOL_EXECUTOR.execute(SearchTaskDispatcher.this);
    }

    @Override
    public void run() {

        onStartDispatch();
        dispatch();
        onStopDispatch();
    }

    private void dispatch() {

        if (this.searchQueue.isEmpty()) {
            onError("缺少查询路径");
            return;
        }

        do {
            while (true) {
                synchronized (this.searchQueue) {
                    try {
                        if (shouldTerminal() || !this.searchQueue.isEmpty()) {
                            break;
                        }
                        this.searchQueue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            do {
                synchronized (this.searchQueue) {
                    if (shouldTerminal()) {
                        break;
                    }
                    if (this.searchQueue.isEmpty()) {
                        break;
                    }
                    String dir = this.searchQueue.poll();
                    TaskRunnable taskRunnable = buildSearchTaskRunnable(dir);
                    executeTask(taskRunnable);

                }
            } while (true);

        } while (!shouldTerminal());
    }

    private void onError(String msg) {
        if (callback != null) callback.onError(null, new IllegalArgumentException(msg));
    }

    private void onStartDispatch() {

        if (beginPaths != null) {
            this.searchQueue.addAll(beginPaths);
        }

        if (callback != null) {
            callback.onStartSearch();
        }
    }

    private void executeTask(TaskRunnable taskRunnable) {
        this.mTaskCount.incrementAndGet();
        TaskExecutors.THREAD_POOL_EXECUTOR.execute(taskRunnable);
    }


    private TaskRunnable buildSearchTaskRunnable(String dir) {
        ISearchTaskEngine task = MediaStoreQueryTaskEngine.getQueryEngine(SearchTaskManager.getContext(), dir);
        if (null != task) {
            return new TaskRunnable(task, dir, this);
        }
        return new TaskRunnable(new FileSearchTaskEngine(), dir, this);
    }

    private synchronized boolean shouldTerminal() {
        if (isTimeout() || isTerminal) {
            return true;
        }

        if (mFinishTaskCount.get() == mTaskCount.get()) {
            return searchQueue.isEmpty();
        }
        return false;
    }

    private boolean isTimeout() {
        if (timeout <= 0) {
            return false;
        }
        return System.currentTimeMillis() - startTime > timeout;
    }


    private void offerTaskPaths(List<PathFile> paths) {

        if (paths == null || paths.size() == 0) {
            return;
        }
        for (int i = 0; i < paths.size(); i++) {
            this.searchQueue.offer(paths.get(i).getDirPath());
        }

    }

    private void wakeQueue() {
        synchronized (this.searchQueue) {
            this.searchQueue.notify();
        }
    }

    @Override
    public void shutdown() {

        this.isTerminal = false;
        this.filters.clear();
        this.searchQueue.clear();
        this.repeatFileFilter.release();
    }


    private synchronized void onStopDispatch() {
        Log.e(TAG, "===>" + mTaskCount.get());
        if (callback != null) {
            callback.onStopSearch();
        }
        DispatcherManager.shareInstance().shutdownDispatcher(this);

    }

    public long getFinishTaskCount() {
        return mFinishTaskCount.get();
    }

    public long getTaskCount() {
        return mTaskCount.get();
    }


    @Override
    public void onTaskStart(String path) {
        Log.d(TAG, "开始扫描路径：" + path);
    }

    @Override
    public void onTaskFiles(String path, SearchResult result) {
        Log.d(TAG, "路径扫描完成：" + path);
        if (result == null) {
            if (callback != null) {
                callback.onFile(new ArrayList<PathFile>());
            }
            wakeQueue();
            return;
        }
        if (callback != null) {
            callback.onFile(onFilterIntercept(result.getFiles()));
        }
        offerTaskPaths(result.getDirs());

        wakeQueue();
    }

    @Override
    public void onTaskFinish(String path) {
        this.mFinishTaskCount.incrementAndGet();
        wakeQueue();
    }

    private List<PathFile> onFilterIntercept(List<PathFile> files) {
        if (filters == null) {
            return files;
        }

        Iterator<PathFile> iterator = files.iterator();
        if (!iterator.hasNext()) return files;

        do {

            PathFile pathFile = iterator.next();

            if (!this.repeatFileFilter.isAcceptable(pathFile.getFile())) {
                iterator.remove();
                continue;
            }

            for (int i = 0; i < filters.size(); i++) {
                IFilter filter = filters.get(i);
                if (!filter.isAcceptable(pathFile.getFile())) {
                    iterator.remove();
                    continue;
                }
            }

            this.repeatFileFilter.addPathToDataSet(pathFile.getFile());

        } while (iterator.hasNext());

        return files;

    }

    @Override
    public void onTaskError(String path, Throwable throwable) {
        Log.e(TAG, "路径扫描出错：" + path);
        if (callback != null) {
            callback.onError(path, throwable);
        }
        wakeQueue();
    }
}
